using System;
using System.Collections.Generic;
using System.Text;

namespace RapidHDL
{
    
    public class PipelineSorter : Component 
    {
        Dictionary<string, PipelineStage> dicOutputs;
        Dictionary<string, int> dicAddresses;

        public PipelineBus InputBus;

        PipelineStage oInputStage;
        ClockComponent oClock;
        NodeVector nvReset;

        NodeVector nvAddress;

        public PipelineSorter(Component poParentComponent, string psName, ClockComponent poClock, NodeVector pnvReset, int piWidth, int piAddressLSB, int piAddressWidth)
            : base(poParentComponent, psName)
        {
            InputBus = new PipelineBus(this, "input_bus", piWidth);
            oInputStage = new PipelineStage(this, "input_stage", poClock, pnvReset, piWidth);
            oInputStage.InputBus.Join(InputBus);
            dicOutputs = new Dictionary<string,PipelineStage>();
            dicAddresses = new Dictionary<string, int>();
            nvReset = pnvReset;
            oClock = poClock;
            nvAddress = this.CreateNodeVector("address", piAddressWidth);
            nvAddress.Join(oInputStage.OutputBus.Data.Subset(this, piAddressLSB, piAddressLSB + piAddressWidth - 1));
        }

        public PipelineBus DefineOutput(string psName, int piAddress)
        {
            return DefineOutput(psName, piAddress, 0, InputBus.Data.Width);
        }

        public PipelineBus DefineOutput(string psName, int piAddress, int piLSB, int piWidth)
        {
            PipelineStage oOutput = new PipelineStage(this, psName, oClock, nvReset, piWidth);
            PipelineReMap oRemap = new PipelineReMap(oInputStage.OutputBus,oOutput.InputBus);
            oRemap.DefineMap(piLSB,piWidth,0,true);
            oRemap.CompleteMap(true);

            dicOutputs.Add(psName, oOutput);
            dicAddresses.Add(psName, piAddress);
            return oOutput.OutputBus;
        }

        public override void GenerateStructure()
        {
            OrGate oStall = new OrGate(this, "stall_gate", dicOutputs.Count);

            oInputStage.OutputBus.Stall.Join(oStall.OutputNodes);

            int iIdx = 0;
            foreach (string sOutput in dicOutputs.Keys)
            {
                PipelineStage oStage = dicOutputs[sOutput];
                int iAddress = dicAddresses[sOutput];

                Constant oConstant = new Constant(this, "const_" + iIdx.ToString(), nvAddress.Width, iAddress);

                /*CompareEqual oCompare = new CompareEqual(this, "compare_" + iIdx.ToString(), nvAddress.Width, nvAddress.Width);//oConstant.OutputNodes.Width);
                oCompare.InputNodesA.Join(nvAddress);
                oCompare.InputNodesB.Join(oConstant.OutputNodes);
                NodeVector nvSelect = this.CreateNodeVector("select_" + iIdx.ToString(),1);
                oCompare.Output.Join(nvSelect);*/
                NodeVector nvSelect = Operators.EQ(this, iAddress, nvAddress);
                nvSelect.Alias("select_" + iIdx.ToString());
                //NodeVector nvWatch = this.CreateNodeVector(,1);
                //nvWatch.Join(nvSelect);
                
                /*AndGate oAnd = new AndGate(this, "and_gate_" + iIdx.ToString(), 2);
                oAnd.InputNodes.Subset(this,0, 0).Join(nvSelect);
                oAnd.InputNodes.Subset(this,1, 1).Join(oInputStage.OutputBus.DataReady);*/
                
                NodeVector nvDataReady = nvSelect & oInputStage.OutputBus.DataReady;
                oStage.InputBus.DataReady.Join(nvDataReady); // vs nvSelect
                NodeVector nvStall = oStage.InputBus.Stall & nvSelect;
                nvStall.Join(oStall.InputNodes.Subset(this,iIdx, iIdx));
                iIdx++;
            }
        }

    }
}
